//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#ifndef RS_REFLECTION_METHOD_HPP
#define RS_REFLECTION_METHOD_HPP

#include "rs/Reflection/Types.hpp"
#include "rs/AnyFxnPointer.hpp"
#include "rs/Assert.hpp"
#include "rs/MetaFacilities.hpp"
#include "rs/Portability.hpp"
#include <vector>

namespace rs
{
    namespace reflection
    {   
        class Method
        {
        public:

            typedef char* NullType;

        public:

            static NullType const kNull;

        public:

            Method();
            
            template< typename TReturn, typename TClass >
            Method(Char8 const* name, TReturn (TClass::*func) ());
            
            template< typename TReturn, typename TClass, typename TArgument1 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5));
            
            template< typename TReturn, typename TClass >
            Method(Char8 const* name, TReturn (TClass::*func) () const);
            
            template< typename TReturn, typename TClass, typename TArgument1 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
            Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) const);
            
            Method(Method const& other);
            
            template< typename TReturn, typename TClass >
            void BindFunction(TReturn (TClass::*func) ());
            
            template< typename TReturn, typename TClass, typename TArgument1 >
            void BindFunction(TReturn (TClass::*func) (TArgument1));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4));
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5));
            
            template< typename TReturn, typename TClass >
            void BindFunction(TReturn (TClass::*func) () const);
            
            template< typename TReturn, typename TClass, typename TArgument1 >
            void BindFunction(TReturn (TClass::*func) (TArgument1) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4) const);
            
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
            void BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) const);

            // No arguments
            template< typename TClass, typename TReturn >
            void operator()(TClass& obj, TReturn* out) const;

            // 1 argument
            template< typename TReturn, typename TClass, typename TArgument1 >
            void operator()(TClass& obj, TArgument1 arg1, TReturn* out) const;

            // 2 arguments
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
            void operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TReturn* out) const;

            // 3 arguments
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
            void operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TArgument3 arg3, TReturn* out) const;
            // 4 arguments
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
            void operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TArgument3 arg3, TArgument4 arg4, TReturn* out) const;

            // 5 arguments
            template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
            void operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TArgument3 arg3, TArgument4 arg4, TArgument5 arg5, TReturn* out) const;
            
            Char8 const* GetName() const;
            
            Type GetReturnType() const;
            
            UInt64 GetRawReturnType() const;
            
            Type GetArgumentType(UInt32 i) const;
            
            UInt64 GetRawArgumentType(UInt32 i) const;
            
            RsSize GetArgumentCount() const;
            
            Boolean operator==(Method const& other) const;
            
            Boolean operator!=(Method const& other) const;
            
        private:
            
            Boolean HasNonVoidReturnType() const;
            
        private:

            std::vector< UInt64 > argumentTypes_; 
            rs::internal::any_fxn_pointer any_;
            Char8 const* name_;
            UInt64 returnType_;
            Boolean isConst_;
        };
    }
}

#define RS_REFLECTION_METHOD_IMPLEMENTATION_GUARD
#include "rs/reflection/Method.ipp"
#undef RS_REFLECTION_METHOD_IMPLEMENTATION_GUARD

#endif // RS_REFLECTION_METHOD_HPP
